package net.siufung.mybatis;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.siufung.core.model.consts.Constants;
import net.siufung.mybatis.log.MybatisLogExecuteTimeService;
import net.siufung.mybatis.log.MybatisLogInterceptor;
import net.siufung.mybatis.metaobject.MybatisMetaObjectHandler;
import net.siufung.mybatis.metaobject.MybatisMetaObjectService;
import net.siufung.mybatis.properties.MybatisProperties;
import net.siufung.mybatis.properties.MybatisProperties.TenantSettings;
import net.siufung.mybatis.tenant.MybatisTenantLineHandler;
import net.siufung.mybatis.tenant.MybatisTenantLineInnerInterceptor;
import net.siufung.security.api.context.TenantContextHolder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Locale;
import java.util.Objects;

/**
 * MybatisPlusConfig
 * @author 陈建峰
 * @since 2022/3/29 3:37 下午
 */
@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties({MybatisProperties.class})
public class MybatisAutoConfig {

    private final MybatisProperties mybatisPlusProperties;
    private final MybatisMetaObjectService mybatisPlusMetaObjectService;
    private final MybatisLogExecuteTimeService sqlLogExecuteTimeService;
    private final DataPermissionHandler dataPermissionHandler;

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        /* 分页插件 */
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(
                DbType.getDbType(mybatisPlusProperties.getDbType())));
        /* 防止全表更新与删除插件 */
        if(mybatisPlusProperties.isBlockAttackEnable()){
            log.info("BlockAttackInnerInterceptor is loaded enabled!");
            interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        }
        /* 乐观锁插件 */
        if(mybatisPlusProperties.isOptimisticLockerEnable()){
            log.info("OptimisticLockerInnerInterceptor is loaded enabled!");
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        }
        /* SQL规范插件 */
        if(mybatisPlusProperties.isIllegalSqlEnable()){
            log.info("IllegalSQLInnerInterceptor is loaded enabled!");
            interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
        }
        /* 多租户插件 */
        TenantSettings tenantSettings = mybatisPlusProperties.getTenant();
        if(tenantSettings.isEnable()){
            log.info("TenantLineInnerInterceptor is loaded  enabled!");
            interceptor.addInnerInterceptor(new MybatisTenantLineInnerInterceptor(new MybatisTenantLineHandler() {
                @Override
                public Expression getTenantId() {
                    return TenantContextHolder.getInstance().getTenantId();
                }
                @Override
                public String getTenantIdColumn() {
                    return Constants.DEFAULT_TENANT_ID.toLowerCase(Locale.ROOT);
                }
                @Override
                public boolean ignoreTable(String tableName) {
                    for (String excludeTable : tenantSettings.getExcludeTables()) {
                        if(StrUtil.equalsIgnoreCase(excludeTable, tableName)){
                            return true;
                        }
                    }
                    return false;
                }
                @Override
                public Boolean validConditions() {
                    return Objects.nonNull(
                            TenantContextHolder.getInstance().getTenantId());
                }
            }));
        }
        /*数据权限*/
        if(mybatisPlusProperties.isDataScopeEnable()){
            log.info("DataPermissionInterceptor is loaded  enabled!");
            interceptor.addInnerInterceptor(new DataPermissionInterceptor(dataPermissionHandler));
        }
        log.info("MybatisAutoConfig is loaded successfully!");
        return interceptor;
    }

    @Bean
    public MetaObjectHandler metaObjectHandler(){
        return new MybatisMetaObjectHandler(mybatisPlusMetaObjectService);
    }

    /**
     * sql 日志
     *
     * @return MybatisLogInterceptor
     */
    @Bean
    @ConditionalOnProperty(value = "mybatis-plus.sql-log", matchIfMissing = true)
    public MybatisLogInterceptor sqlLogInterceptor() {
        return new MybatisLogInterceptor(sqlLogExecuteTimeService);
    }

}
